#!/usr/bin/env python3
import os
from flask import Flask, request, render_template, flash, jsonify
from mongoengine import DoesNotExist
from flask_mongoengine import MongoEngine, MongoEngineSessionInterface
from flask_login import LoginManager, login_user, login_required, logout_user
from flask_bcrypt import Bcrypt
from werkzeug.utils import redirect

from models import User, Game
from forms import LoginForm, RegisterForm, CreateGameForm


app = Flask(__name__)

# 数据库设置，默认数据库名gamux
app.config['MONGODB_SETTINGS'] = {'DB': 'gamux'}

# 从环境变量中读取session加密秘钥，如果没有SECRET_KEY环境变量则用默认的秘钥
# 上线时应该在服务器上设置SECRET_KEY保证安全
DEFAULT_SECRET_KEY = '(+0d6)=@y(iw0)=ks%rme%9ubp=(l4p$ure5%8&j$nmpep(+#r'
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', DEFAULT_SECRET_KEY)

# 连接mongodb。可通过db.connection访问到原始的pymongo.MongoClient对象
db = MongoEngine(app)
# 使用MongoEngine自带的SessionInterface，把Session保存在mongodb内
app.session_interface = MongoEngineSessionInterface(db)

# 用flask-bcrypt提供加盐加密功能
bcrypt = Bcrypt(app)

# 初始化login_manager，用flask-login扩展管理权限
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.login_message = "需要登陆才能访问本页面。"


@login_manager.user_loader
def load_user(user_id):
    try:
        user = User.objects.get(id=user_id)
    except DoesNotExist:
        user = None
    return user


def validate_form_with_log(form):
    """验证表单，记录log

    WTForms的错误信息保存在form.errors里，是一个[field][err]的二维数组，不方便记录log。

    :param form: request.form
    :return: 等于
    """
    if form.validate_on_submit():
        return True

    if not request.method == "GET":
        app.logger.debug("表单验证失败")
        for field in form.errors:
            for error in form.errors[field]:
                app.logger.debug('%s: %s' % (field, error))
    return False


@app.route("/login", methods=["GET", "POST"])
def login():
    error = None
    form = LoginForm(request.form)
    # 验证表单，同时也会验证请求类型是否是POST或PUT
    if validate_form_with_log(form):
        try:
            # 查找用户
            user = User.objects.get(email=form.email.data)
            # 验证密码
            if bcrypt.check_password_hash(user.password, form.password.data):
                remember = request.form.get("remember", "no") == "yes"
                if login_user(user, remember=remember):
                    flash("登陆成功")
                    return redirect(request.args.get('next') or '/')
        except DoesNotExist as e:
            app.logger.debug(e)
            error = "用户名或密码错误"
    elif request.method == "POST":
        error = "表单验证失败"
    return render_template("/login.html", form=form, error=error)


@app.route("/register", methods=["GET", "POST"])
def register():
    error = None
    form = RegisterForm(request.form)
    if validate_form_with_log(form):
        email = form.email.data
        password_hash = bcrypt.generate_password_hash(form.password.data)
        user = User(email=email, password=password_hash)
        try:
            user.save()
            if login_user(user, remember=False):
                flash("已经登陆")
                return redirect('/')
            error = "无法登陆"
        except Exception as e:
            app.logger.error(e)
            error = "无法使用这个email地址注册"
    elif request.method == "POST":
        error = "表单验证失败"
    return render_template("/register.html", form=form, error=error)


@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect('/')


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/games/')
def show_game_list():
    return jsonify(games=Game.objects)


@app.route('/games/add/', methods=["GET", "POST"])
@login_required
def add_new_game():
    error = None
    form = CreateGameForm(request.form)
    if validate_form_with_log(form):
        title = form.title.data
        title_translation = form.title_translation.data
        developer = form.developer.data
        developer_site = form.developer_site.data
        intro = form.intro.data
        game = Game(title=title, title_translation=title_translation, developer=developer,
                    developer_site=developer_site, intro=intro)
        try:
            game.save()
            return redirect('/games')
        except Exception as e:
            app.logger.error(e)
            error = "无法保存游戏信息"
    elif request.method == "POST":
        error = "表单验证失败"
    return render_template("/gamux-admin/new-post.html", form=form, error=error)


if __name__ == '__main__':
    app.run(debug=True, port=4000)